﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Collections;

namespace CitrixPVSWrapper
{

    [System.Management.Automation.Cmdlet("RunWithReturn", "PvsReboot", SupportsShouldProcess = true)]
    public class Run_PvsReboot : System.Management.Automation.PSCmdlet
    {

        [Parameter(ParameterSetName = "Device", Mandatory = true)]
        public PvsDevice[] Device;

        [Parameter(ParameterSetName = "DeviceId", ValueFromPipelineByPropertyName = true, Mandatory = true)]
        [Alias("Id")]
        public String[] DeviceId;

        [Parameter(ParameterSetName = "DeviceName", Mandatory = true)]
        [Alias("Name")]
        public String[] DeviceName;

        [Parameter(ParameterSetName = "DeviceMac", Mandatory = true)]
        public String[] DeviceMac;

        [Parameter(ParameterSetName = "Collection", Mandatory = true)]
        public PvsCollection Collection;

        [Parameter(ParameterSetName = "CollectionId", ValueFromPipelineByPropertyName = true, Mandatory = true)]
        public string CollectionId;

        [Parameter(ParameterSetName = "CollectionName", Mandatory = true)]
        [Parameter(ParameterSetName = "CollectionName-SiteName", Mandatory = true)]
        [Parameter(ParameterSetName = "CollectionName-SiteId", Mandatory = true)]
        [Parameter(ParameterSetName = "CollectionName-Site", Mandatory = true)]
        public string CollectionName;

        [Parameter(ParameterSetName = "SiteView", Mandatory = true)]
        public PvsSiteView SiteView;

        [Parameter(ParameterSetName = "SiteViewId", ValueFromPipelineByPropertyName = true, Mandatory = true)]
        public string SiteViewId;

        [Parameter(ParameterSetName = "SiteViewName", Mandatory = true)]
        [Parameter(ParameterSetName = "SiteViewName-SiteName", Mandatory = true)]
        [Parameter(ParameterSetName = "SiteViewName-SiteId", Mandatory = true)]
        [Parameter(ParameterSetName = "SiteViewName-Site", Mandatory = true)]
        public string SiteViewName;

        [Parameter(ParameterSetName = "DiskLocator", Mandatory = true)]
        public PvsDiskLocator DiskLocator;

        [Parameter(ParameterSetName = "DiskLocatorId", ValueFromPipelineByPropertyName = true, Mandatory = true)]
        public string DiskLocatorId;

        [Parameter(ParameterSetName = "DiskLocatorName", Mandatory = true)]
        [Parameter(ParameterSetName = "DiskLocatorName-SiteName", Mandatory = true)]
        [Parameter(ParameterSetName = "DiskLocatorName-SiteId", Mandatory = true)]
        [Parameter(ParameterSetName = "DiskLocatorName-Site", Mandatory = true)]
        [Parameter(ParameterSetName = "DiskLocatorName-StoreName", Mandatory = true)]
        [Parameter(ParameterSetName = "DiskLocatorName-StoreId", Mandatory = true)]
        [Parameter(ParameterSetName = "DiskLocatorName-Store", Mandatory = true)]
        public string DiskLocatorName;
        
        [Parameter(ParameterSetName = "CollectionName-Site", Mandatory = true)]
        [Parameter(ParameterSetName = "SiteViewName-Site", Mandatory = true)]
        [Parameter(ParameterSetName = "DiskLocatorName-Site", Mandatory = true)]
        public PvsSite Site;

        [Parameter(ParameterSetName = "CollectionName-SiteId", Mandatory = true)]
        [Parameter(ParameterSetName = "SiteViewName-SiteId", Mandatory = true)]
        [Parameter(ParameterSetName = "DiskLocatorName-SiteId", Mandatory = true)]
        public string SiteId;

        [Parameter(ParameterSetName = "CollectionName-SiteName", Mandatory = true)]
        [Parameter(ParameterSetName = "SiteViewName-SiteName", Mandatory = true)]
        [Parameter(ParameterSetName = "DiskLocatorName-SiteName", Mandatory = true)]
        public string SiteName;

        [Parameter(ParameterSetName = "DiskLocatorName-Store", Mandatory = true)]
        public PvsStore Store;

        [Parameter(ParameterSetName = "DiskLocatorName-StoreId", Mandatory = true)]
        public string StoreId;

        [Parameter(ParameterSetName = "DiskLocatorName-StoreName", Mandatory = true)]
        public string StoreName;

        [Parameter(ParameterSetName = "FarmView", Mandatory = true)]
        public PvsFarmView FarmView;

        [Parameter(ParameterSetName = "FarmViewId", ValueFromPipelineByPropertyName = true, Mandatory = true)]
        public string FarmViewId;

        [Parameter(ParameterSetName = "FarmViewName", Mandatory = true)]
        public string FarmViewName;

        [Parameter(Mandatory = false)]
        public String Message;

        [Parameter(Mandatory = false)]
        public Int32 Delay = 10;

        [Parameter(ParameterSetName = "DiskLocator", Mandatory = false)]
        [Parameter(ParameterSetName = "DiskLocatorId", Mandatory = false)]
        [Parameter(ParameterSetName = "DiskLocatorName", Mandatory = false)]
        [Parameter(ParameterSetName = "DiskLocatorName-SiteName", Mandatory = false)]
        [Parameter(ParameterSetName = "DiskLocatorName-SiteId", Mandatory = false)]
        [Parameter(ParameterSetName = "DiskLocatorName-Site", Mandatory = false)]
        [Parameter(ParameterSetName = "DiskLocatorName-StoreName", Mandatory = false)]
        [Parameter(ParameterSetName = "DiskLocatorName-StoreId", Mandatory = false)]
        [Parameter(ParameterSetName = "DiskLocatorName-Store", Mandatory = false)]
        public Int32 Version;

        protected override void ProcessRecord()
        {
            // Verbose and Debug Settings
            Boolean Verbose = this.MyInvocation.BoundParameters.ContainsKey("Verbose");
            WriteVerbose("Verbose Flag: " + Verbose);
            Boolean Debug = this.MyInvocation.BoundParameters.ContainsKey("Debug");
            WriteDebug("Debug Flag: " + Debug);

            // Collections to hold the queries and the results
            Collection<QueryDefinition> RunQueries = new Collection<QueryDefinition>();
            string TargetObject = "Reboot";

            // Build the queries based on the parameter sets
            switch (this.ParameterSetName)
            {
                case "Device":
                    {
                        foreach (PvsDevice Dev in Device)
                        {
                            RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DeviceId=\"" + Dev.DeviceId + "\""));
                        }
                        break;
                    }

                case "DeviceId":
                    {
                        foreach (String DevId in DeviceId)
                        {
                            RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DeviceId=\"" + DevId + "\""));
                        }
                        break;
                    }

                case "DeviceName":
                    {
                        foreach (String DevName in DeviceName)
                        {
                            foreach (PvsDevice QryDevice in Get_PvsDevice_Internal.Get_DeviceByName(DevName, Verbose))
                            { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DeviceId=\"" + QryDevice.DeviceId + "\"")); }
                        }
                        break;
                    }

                case "DeviceMac":
                    {
                        foreach (String DevMac in DeviceMac)
                        {
                            foreach (PvsDevice QryDevice in Get_PvsDevice_Internal.Get_DeviceByName(DevMac, Verbose))
                            { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DeviceId=\"" + QryDevice.DeviceId + "\"")); }
                        }
                        break;
                    }

                case "Collection":
                    {
                        RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "CollectionId=\"" + this.Collection.CollectionId + "\""));
                        break;
                    }

                case "CollectionId":
                    {
                        RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "CollectionId=\"" + this.CollectionId + "\""));
                        break;
                    }

                case "CollectionName":
                    {
                        foreach (PvsCollection QryCollection in Get_PvsCollection_Internal.Get_CollectionByName(this.CollectionName, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "CollectionId=\"" + QryCollection.CollectionId + "\"")); }
                        break;
                    }

                case "CollectionName-Site":
                    {
                        foreach (PvsCollection QryCollection in Get_PvsCollection_Internal.Get_CollectionByNameAndSiteId(this.CollectionName, this.Site.SiteId, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "CollectionId=\"" + QryCollection.CollectionId + "\"")); }
                        break;
                    }

                case "CollectionName-SiteId":
                    {
                        foreach (PvsCollection QryCollection in Get_PvsCollection_Internal.Get_CollectionByNameAndSiteId(this.CollectionName, this.SiteId, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "CollectionId=\"" + QryCollection.CollectionId + "\"")); }
                        break;
                    }

                case "CollectionName-SiteName":
                    {
                        foreach (PvsCollection QryCollection in Get_PvsCollection_Internal.Get_CollectionByNameAndSiteName(this.CollectionName, this.SiteName, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "CollectionId=\"" + QryCollection.CollectionId + "\"")); }
                        break;
                    }

                case "SiteView":
                    {
                        RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "SiteViewId=\"" + this.SiteView.SiteViewId + "\""));
                        break;
                    }

                case "SiteViewId":
                    {
                        RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "SiteViewId=\"" + this.SiteViewId + "\""));
                        break;
                    }

                case "SiteViewName":
                    {
                        foreach (PvsSiteView QrySiteView in Get_PvsSiteView_Internal.Get_SiteViewByName(this.SiteViewName, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "SiteViewId=\"" + QrySiteView.SiteViewId + "\"")); }
                        break;
                    }

                case "SiteViewName-Site":
                    {
                        foreach (PvsSiteView QrySiteView in Get_PvsSiteView_Internal.Get_SiteViewByNameAndSiteId(this.SiteViewName, this.Site.SiteId, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "SiteViewId=\"" + QrySiteView.SiteViewId + "\"")); }
                        break;
                    }

                case "SiteViewName-SiteId":
                    {
                        foreach (PvsSiteView QrySiteView in Get_PvsSiteView_Internal.Get_SiteViewByNameAndSiteId(this.SiteViewName, this.SiteId, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "SiteViewId=\"" + QrySiteView.SiteViewId + "\"")); }
                        break;
                    }

                case "SiteViewName-SiteName":
                    {
                        foreach (PvsSiteView QrySiteView in Get_PvsSiteView_Internal.Get_SiteViewByNameAndSiteName(this.SiteViewName, this.SiteName, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "SiteViewId=\"" + QrySiteView.SiteViewId + "\"")); }
                        break;
                    }

                case "DiskLocator":
                    {
                        RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DiskLocatorId=\"" + this.DiskLocator.DiskLocatorId + "\""));
                        break;
                    }

                case "DiskLocatorId":
                    {
                        RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DiskLocatorId=\"" + this.DiskLocatorId + "\""));
                        break;
                    }

                case "DiskLocatorName":
                    {
                        foreach (PvsDiskLocator QryDiskLocator in Get_PvsDiskLocator_Internal.Get_DiskLocatorByName(this.DiskLocatorName, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DiskLocatorId=\"" + QryDiskLocator.DiskLocatorId + "\"")); }
                        break;
                    }

                case "DiskLocatorName-Site":
                    {
                        foreach (PvsDiskLocator QryDiskLocator in Get_PvsDiskLocator_Internal.Get_DiskLocatorByNameAndSiteId(this.DiskLocatorName, this.Site.SiteId, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DiskLocatorId=\"" + QryDiskLocator.DiskLocatorId + "\"")); }
                        break;
                    }

                case "DiskLocatorName-SiteId":
                    {
                        foreach (PvsDiskLocator QryDiskLocator in Get_PvsDiskLocator_Internal.Get_DiskLocatorByNameAndSiteId(this.DiskLocatorName, this.SiteId, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DiskLocatorId=\"" + QryDiskLocator.DiskLocatorId + "\"")); }
                        break;
                    }

                case "DiskLocatorName-SiteName":
                    {
                        foreach (PvsDiskLocator QryDiskLocator in Get_PvsDiskLocator_Internal.Get_DiskLocatorByNameAndSiteName(this.DiskLocatorName, this.SiteName, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DiskLocatorId=\"" + QryDiskLocator.DiskLocatorId + "\"")); }
                        break;
                    }

                case "DiskLocatorName-Store":
                    {
                        foreach (PvsDiskLocator QryDiskLocator in Get_PvsDiskLocator_Internal.Get_DiskLocatorByNameAndStoreId(this.DiskLocatorName, this.Store.StoreId, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DiskLocatorId=\"" + QryDiskLocator.DiskLocatorId + "\"")); }
                        break;
                    }

                case "DiskLocatorName-StoreId":
                    {
                        foreach (PvsDiskLocator QryDiskLocator in Get_PvsDiskLocator_Internal.Get_DiskLocatorByNameAndStoreId(this.DiskLocatorName, this.StoreId, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DiskLocatorId=\"" + QryDiskLocator.DiskLocatorId + "\"")); }
                        break;
                    }

                case "DiskLocatorName-StoreName":
                    {
                        foreach (PvsDiskLocator QryDiskLocator in Get_PvsDiskLocator_Internal.Get_DiskLocatorByNameAndStoreName(this.DiskLocatorName, this.StoreName, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "DiskLocatorId=\"" + QryDiskLocator.DiskLocatorId + "\"")); }
                        break;
                    }

                case "FarmView":
                    {
                        RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "FarmViewId=\"" + this.FarmView.FarmViewId + "\""));
                        break;
                    }

                case "FarmViewId":
                    {
                        RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "FarmViewId=\"" + this.FarmViewId + "\""));
                        break;
                    }

                case "FarmViewName":
                    {
                        foreach (PvsFarmView QryFarmView in Get_PvsFarmView_Internal.Get_FarmViewByName(this.FarmViewName, Verbose))
                        { RunQueries.Add(new QueryDefinition(QueryActionType.RunWithReturn, TargetObject, "FarmViewId=\"" + QryFarmView.FarmViewId + "\"")); }
                        break;
                    }
            }

            // Test that we have a suitable object to perform the Run with, and build the confirmation message
            if (RunQueries.Count() == 0)
            {
                WriteError(new ErrorRecord(new InvalidOperationException("No items were found to reboot using the supplied parameters"), "1", ErrorCategory.InvalidArgument, null));
            }

            // Execute the run queries
            foreach (QueryDefinition RunQuery in RunQueries)
            {
                // Add in the optional parameters
                if (MyInvocation.BoundParameters.ContainsKey("Message")) { RunQuery.Parameters.Add("message=\"" + this.Message + "\""); }
                if (MyInvocation.BoundParameters.ContainsKey("Delay")) { RunQuery.Parameters.Add("delay=\"" + this.Delay+ "\""); }
                if (MyInvocation.BoundParameters.ContainsKey("Version")) { RunQuery.Parameters.Add("version=\"" + this.Version + "\""); }

                string objectId;
                string objectName;
                string ShouldProcessMessage;

                // Check that we have a valid parameter to work with, extract the object id/name and build the ShouldProcess message
                if (RunQuery.Parameters.Where(p => p.StartsWith("DeviceId")).Count() == 1)
                {
                    objectId = RunQuery.Parameters.Where(p => p.StartsWith("DeviceId")).First().Split('=')[1].Replace('"', ' ').Trim();
                    if (Get_PvsDevice_Internal.Get_DeviceById(objectId).Count() != 1)
                    {
                        WriteError(new ErrorRecord(new InvalidOperationException(String.Format("Device Id {0} does not exist", objectId)), "2", ErrorCategory.InvalidArgument, null));
                        continue;
                    }
                    objectName = Get_PvsDevice_Internal.Get_DeviceById(objectId).First().DeviceName;
                    ShouldProcessMessage = String.Format("Reboot device {0} with device Id {1}", objectName, objectId);
                }

                else if (RunQuery.Parameters.Where(p => p.StartsWith("CollectionId")).Count() == 1)
                {
                    objectId = RunQuery.Parameters.Where(p => p.StartsWith("CollectionId")).First().Split('=')[1].Replace('"', ' ').Trim();
                    if (Get_PvsCollection_Internal.Get_CollectionById(objectId).Count() != 1)
                    {
                        WriteError(new ErrorRecord(new InvalidOperationException(String.Format("Collection Id {0} does not exist", objectId)), "2", ErrorCategory.InvalidArgument, null));
                        continue;
                    }
                    objectName = Get_PvsCollection_Internal.Get_CollectionById(objectId).First().CollectionName;
                    ShouldProcessMessage = String.Format("Reboot all devices in collection {0} with collection Id {1}", objectName, objectId);
                }

                else if (RunQuery.Parameters.Where(p => p.StartsWith("SiteViewId")).Count() == 1)
                {
                    objectId = RunQuery.Parameters.Where(p => p.StartsWith("SiteViewId")).First().Split('=')[1].Replace('"', ' ').Trim();
                    if (Get_PvsSiteView_Internal.Get_SiteViewById(objectId).Count() != 1)
                    {
                        WriteError(new ErrorRecord(new InvalidOperationException(String.Format("SiteView Id {0} does not exist", objectId)), "2", ErrorCategory.InvalidArgument, null));
                        continue;
                    }
                    objectName = Get_PvsSiteView_Internal.Get_SiteViewById(objectId).First().SiteViewName;
                    ShouldProcessMessage = String.Format("Reboot all devices in site view {0} with site view Id {1}", objectName, objectId);
                }

                else if (RunQuery.Parameters.Where(p => p.StartsWith("DiskLocatorId")).Count() == 1)
                {
                    objectId = RunQuery.Parameters.Where(p => p.StartsWith("DiskLocatorId")).First().Split('=')[1].Replace('"', ' ').Trim();
                    if (Get_PvsDiskLocator_Internal.Get_DiskLocatorById(objectId).Count() != 1)
                    {
                        WriteError(new ErrorRecord(new InvalidOperationException(String.Format("DiskLocator Id {0} does not exist", objectId)), "2", ErrorCategory.InvalidArgument, null));
                        continue;
                    }
                    objectName = Get_PvsDiskLocator_Internal.Get_DiskLocatorById(objectId).First().DiskLocatorName;
                    ShouldProcessMessage = String.Format("Reboot all devices assigned to disk locator {0} with disk locator Id {1}", objectName, objectId);
                }

                else if (RunQuery.Parameters.Where(p => p.StartsWith("FarmViewId")).Count() == 1)
                {
                    objectId = RunQuery.Parameters.Where(p => p.StartsWith("FarmViewId")).First().Split('=')[1].Replace('"', ' ').Trim();
                    if (Get_PvsFarmView_Internal.Get_FarmViewById(objectId).Count() != 1)
                    {
                        WriteError(new ErrorRecord(new InvalidOperationException(String.Format("Farm View Id {0} does not exist", objectId)), "2", ErrorCategory.InvalidArgument, null));
                        continue;
                    }
                    objectName = Get_PvsFarmView_Internal.Get_FarmViewById(objectId).First().FarmViewName;
                    ShouldProcessMessage = String.Format("Reboot all devices in farm view {0} with farm view Id {1}", objectName, objectId);
                }

                else
                {
                    WriteError(new ErrorRecord(new InvalidOperationException("Query does not contain any valid targets"), "2", ErrorCategory.InvalidArgument, null));
                    continue;
                }

                // Perform the actual run and return any errors
                if (ShouldProcess(ShouldProcessMessage))
                {
                    foreach (string[] Record in PvsHelpers.ExecuteMcliQuery(RunQuery, Verbose, Debug))
                    {
                        WriteDebug("Returning Object");
                        WriteObject(PvsHelpers.Get_StringValueFromRecord("Devices", Record, Verbose));
                    }
                }

            }
        }
    }
}
